/* 
 * File:   Properties.cpp
 * Author: ferranferri
 * 
 * Created on May 14, 2013, 5:05 PM
 */

#include <math.h>


#include <fstream>
#include <vector>
#include <stdexcept>
#include "Properties.h"
#include "cuUtils.h"


namespace z3d {
    namespace utils {

        Properties::Properties(const std::string& fileName, bool readonly)
                :_fileName(fileName)
                ,_readOnly(readonly)
                ,_log(glog::CGlobalLog::getSingleton())
                ,_docRoot(NULL)
                ,_parent(NULL)
        {}
        
        Properties::Properties(const Properties& p)
                :_fileName(p._fileName)
                ,_readOnly(p._readOnly)
                ,_log(p._log)
                ,_docRoot(p._docRoot)
                ,_parent(&p)
        {}
        
        bool Properties::processFile() {
            _log.DEBUG_MSG(UTILS_APPENDER, "Processing file %s", _fileName.c_str());
            if(_fileName.length() == 0) {
                _log.WARNING_MSG(UTILS_APPENDER, "Filename to process is empty. Exiting processFile...");
                return false;
            }
            //trying to open filename
            std::ifstream myFile;
            myFile.open(_fileName.c_str(), std::ios::in);
            if(!myFile.good()) {
                _log.WARNING_MSG(UTILS_APPENDER, "Cannot open file %s", _fileName.c_str());
                return false;
            }
            // parsing json
            _docRoot = new Json::Value();
            Json::Reader reader;
            bool succesParsing = reader.parse(myFile, *_docRoot);
            myFile.close();
            
            //something goes wrong???
            if(!succesParsing) {
                // report to the user the failure and their locations in the document.
                _log.WARNING_MSG(UTILS_APPENDER, "Failed to parse configuration. \n\t Message: %s",reader.getFormattedErrorMessages().c_str() );
                return false;
            }
            return true;
        }
        
        std::string Properties::getPropertyAsString(const std::string& propName, const std::string& defaultValue) {
            try {
                Json::Value res = getPropertyAsObject(propName, NULL, *_docRoot);
                if(res.empty()) {
                    return defaultValue;
                }
                return res.asString();
            }
            catch(const std::runtime_error& e) {
                THROW_AND_LOG(UTILS_APPENDER, core::resourceTypeConversionException, e.what() );
            }
            return "";
        }
                
        uint32_t Properties::getPropertyAsUnsigned(const std::string& propName, const uint32_t& defaultValue) {
            try {
                Json::Value res = getPropertyAsObject(propName, NULL, *_docRoot);
                if(res.empty()) {
                    return defaultValue;
                }
                return res.asUInt();
            }
            catch(std::runtime_error e) {
                THROW_AND_LOG(UTILS_APPENDER, core::resourceTypeConversionException, e.what() );
            }
            return 0;
        }
        
        int32_t Properties::getPropertyAsSigned(const std::string& propName, const int32_t& defaultValue) {
                try {
                Json::Value res = getPropertyAsObject(propName, NULL, *_docRoot);
                if(res.empty()) {
                    return defaultValue;
                }
                return res.asInt();
            }
            catch(std::runtime_error e) {
                THROW_AND_LOG(UTILS_APPENDER, core::resourceTypeConversionException, e.what() );
            }
            return 0;
        }
     
        float Properties::getPropertyAsDouble(const std::string& propName, const float& defaultValue) {
                try {
                Json::Value res = getPropertyAsObject(propName, NULL, *_docRoot);
                if(res.empty()) {
                    return defaultValue;
                }
                return res.asFloat();
            }
            catch(std::runtime_error e) {
                THROW_AND_LOG(UTILS_APPENDER, core::resourceTypeConversionException, e.what() );
            }
            return 0;
        }
        
        bool Properties::getPropertyAsBoolean(const std::string& propName, const bool& defaultValue)  {
                try {
                Json::Value res = getPropertyAsObject(propName, NULL, *_docRoot);
                if(res.empty()) {
                    return defaultValue;
                }
                return res.asBool();
            }
            catch(std::runtime_error e) {
                THROW_AND_LOG(UTILS_APPENDER, core::resourceTypeConversionException, e.what() );
            }
            return 0;
        }
                    
        bool Properties::propertyExists(const Json::Value::Members& m, const std::string& prop) {
            Json::Value::Members::const_iterator ci = m.begin();
            for(; ci != m.end(); ++ci )
            {
                if(*ci == prop) {
                    return true;
                }
            }
            return false;
        }
        
        bool Properties::exists(const std::string& prop) {
            return propertyExists(this->_docRoot->getMemberNames(), prop);
        }
        
        bool Properties::isPath(const std::string& property) {
            if(property.find('/') != std::string::npos)
                return true;
            return false;
        }
        
        Properties* Properties::getChild(const std::string& property) {
            Json::Value tmp(_docRoot->get(property, ""));
            if(tmp.empty())
                return NULL;
            Properties* p = new Properties(*this);
            p->_docRoot = new Json::Value(tmp);
            return p;
        }
        
        const Json::Value Properties::getPropertyAsObject(const std::string& propName, const Json::Value* defaultValue, const Json::Value root) {
            // strip the leading slash
            std::string prop(propName);
            if(propName[0] == '/') {
                prop = prop.substr(1, std::string::npos);
            }
            //If not a path return the direct result
            if(!isPath(propName)) {
                return root.get(propName, defaultValue);
            }
            Json::Value newRoot = root.get(StringHelper::getSubstr("", "/", propName), defaultValue);
            return getPropertyAsObject(StringHelper::getSubstr("/", "", propName), defaultValue, newRoot);
        }
        
        Properties::~Properties() {
            if(_docRoot != NULL) {
                delete _docRoot;
            }
        }

    }
} 